package com.cty.aio;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;

/**
 * @Auther: cty
 * @Date: 2020/4/18 15:56
 * @Description: AIO客户端
 * AIO Asynchronous IO 异步非阻塞编程方式 JDK7开始支持
 * @version: 1.0
 */
public class AIOClient {
    private AsynchronousSocketChannel channel;

    public AIOClient(String host, int port) {
        init(host, port);
    }

    private void init(String host, int port){
        try {
            channel = AsynchronousSocketChannel.open();
            /**
             * 属类：AsynchronousSocketChannel
             * 方法：public abstract Future<Void> connect(SocketAddress remote)
             * 参数：remote-远程通道地址
             * 返回：一个Future<V>对象，用于获取连接成功的结果（get方法，连接成功返回null），取消连接任务以及
             *      判断连接任务是否正在运行或取消
             * 作用：发起建立连接任务，非阻塞方法-会直接返回一个Future<V>对象
             * 说明：
             *      Future的get方法是阻塞的，一旦调用就一直等待建立连接，如果连接建立get返回null
             */
            channel.connect(new InetSocketAddress(host, port));
            System.out.println("Client started.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void read(){
        System.out.println("Do reading ... ");
        try {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.clear();
            /**
             * 属类：AsynchronousSocketChannel
             * 方法：public abstract Future<Integer> read(ByteBuffer dst)
             * 参数：dst-读取数据放到给定缓存
             * 返回：一个Future<V>对象，用于获取读取的数据的字节数（get方法），取消读任务以及判断读任务是否正在运行或取消
             * 作用：发起读任务，非阻塞方法-会直接返回一个Future<V>对象。
             * 说明：
             *      始终在一个线程里运行。
             *
             *      Future的get方法是阻塞的，一旦调用就一直等待读取完成，如果读取完成get返回读取的数据字节数，到了读数据流
             *      末尾没有读取到数据则返回-1。
             */
            channel.read(buffer).get();  // read是非阻塞方法，get是阻塞方法，不阻塞一下会读到空值
            buffer.flip();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            System.out.println("Reading from "+channel.getRemoteAddress()+" server: "+new String(bytes,"UTF-8"));
        }  catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
//            e.printStackTrace();
            System.out.println("Connection reset.");
            this.doDestory();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
//            e.printStackTrace();
            System.out.println("Connection reset.");
            this.doDestory();
        }
    }

    private void write(String line){
        System.out.println("Do writing ... ");
        try {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.put(line.getBytes("UTF-8"));
            buffer.flip();
            /**
             * 属类：AsynchronousSocketChannel
             * 方法：public abstract Future<Integer> write(ByteBuffer src)
             * 参数：dst-存放待写出数据的给定缓存
             * 返回：一个Future<V>对象，用于获取写出数据的字节数（get方法），取消写任务以及判断写任务是否正在运行或取消
             * 作用：发起写任务，非阻塞方法-会直接返回一个Future<V>对象。
             * 说明：
             *      始终在一个线程里运行。
             *
             *      Future的get方法是阻塞的，一旦调用就一直等待写出完成，如果读取完成get返回写出的数据字节数。
             */
            channel.write(buffer);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public void doDestory(){
        if(null != channel){
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        AIOClient client = new AIOClient("localhost", 9999);
        while (true){
            try {
                System.out.println("Enter message send to "+client.channel.getRemoteAddress()+" server > ");
                Scanner scanner = new Scanner(System.in);
                String message = scanner.nextLine();

                if(message.equals("exit")){
                    System.out.println("Client exit.");
                    client.doDestory();
                    break;
                }

                client.write(message);  // 写
                client.read();  // 读

            } catch (IOException e) {
//                e.printStackTrace();
                System.out.println("Connection reset.");
                client.doDestory();
                break;
            }
        }
    }
}
